Skip to content

feat(chat): VS Code-style file path context menu on change lists and tools#371

Open
hkx1997 wants to merge 8 commits into
xintaofei:mainfrom
hkx1997:feat/file-path-context-menu
Open

feat(chat): VS Code-style file path context menu on change lists and tools#371
hkx1997 wants to merge 8 commits into
xintaofei:mainfrom
hkx1997:feat/file-path-context-menu

Conversation

@hkx1997

@hkx1997 hkx1997 commented Jul 22, 2026

Copy link
Copy Markdown

Summary

Shared VS Code-style file path context menu across chat surfaces, absolute-path tooltips for tool titles (especially Read), and short-lived toast feedback for menu actions.

Surfaces

Surface Behavior
Message navigator (session changed files) Right-click menu
Reply artifacts (new / changed files at turn end) Right-click menu
Tool-row headers (collapsed Read / Edit / Write) Right-click menu + absolute path on hover
Expanded Read/Write path links (FilePathLink) Right-click menu + absolute path tooltip
Other FilePathLink (e.g. diff headers) Same menu

Menu actions

  • Open in Codeg
  • Add to chat — inserts @file badge via existing emitAttachFileToSession (same as file tree “Add to session”)
  • Reveal in Explorer / Finder (local desktop)
  • Open with → default app / VS Code / Cursor
  • Copy relative path / absolute path / file name

Fixes included

  • Nested right-click: conversation panel ContextMenu yields under [data-file-path-menu] (no longer steals tool-row / path-link menus)
  • Read title tooltip: shared deep path lookup; absolute native path on outer wrapper + trigger + header row (was stuck as Read AGENTS.md)
  • Expanded Read path link + menu: recover nested/truncated Read inputs so the card always gets a real FilePathLink (Edit / file lists were already fine)
  • Toast auto-dismiss: success feedback uses a short duration (~2.5s) so copy/add-to-chat toasts clear instead of sitting for the workspace default (15s)
  • openPath accepts optional openWith for VS Code / Cursor
  • toNativeAbsoluteFilePath for clipboard-friendly absolute paths

i18n & tests

  • Folder.chat.filePathMenu in all 10 locales
  • Unit tests: path helpers, FilePathLink, message nav

Test plan

  • pnpm test (path helpers, link-safety, message nav)
  • pnpm eslint on touched sources
  • Local Windows NSIS package + manual install test by author
  • Message navigator file rows → file menu (not conversation export menu)
  • Reply artifacts new/changed files → same menu
  • Tool header Edit / Read → absolute path tooltip + file menu
  • Expanded Read path link → tooltip + right-click menu
  • Add to chat inserts @file into composer
  • Copy / add-to-chat toasts appear bottom-right and auto-dismiss (~2.5s)

@hkx1997

hkx1997 commented Jul 22, 2026

Copy link
Copy Markdown
Author

Withdrawing to fix message-navigator context menu. Will reopen when fixed.

@hkx1997 hkx1997 closed this Jul 22, 2026
@hkx1997 hkx1997 reopened this Jul 22, 2026
@hkx1997 hkx1997 changed the title feat(chat): VS Code-style context menu on changed-file lists feat(chat): VS Code-style file path context menu on change lists and tools Jul 22, 2026
@hkx1997

hkx1997 commented Jul 23, 2026

Copy link
Copy Markdown
Author

@xintaofei 佬,增加了右键快捷功能
image

@xintaofei

Copy link
Copy Markdown
Owner

当前分支有冲突

hkx1997 added 3 commits July 23, 2026 21:34
Add a shared path context menu on the message navigator and reply
artifacts cards: open in Codeg, add to chat as an @file badge, reveal
in the system file manager, open with default app / VS Code / Cursor,
and copy relative / absolute / file-name paths.

Stop right-click propagation so the outer conversation panel menu no
longer steals the event. Extend openPath to accept openWith for local
desktop openers, and resolve native absolute paths for clipboard copy.
Follow-up to the changed-file context menu: claim right-clicks on tool
headers so the conversation panel menu no longer steals them, show full
absolute paths on Read/Edit title hover, and restore path links plus the
same menu inside expanded Read/Write cards via shared path lookup.
Use-short-duration-for-copy-and-add-to-chat-success-toasts
@hkx1997
hkx1997 force-pushed the feat/file-path-context-menu branch from 264e15d to ff8fb72 Compare July 23, 2026 13:35
@hkx1997

hkx1997 commented Jul 23, 2026

Copy link
Copy Markdown
Author

当前分支有冲突

可以了,佬

@xintaofei

Copy link
Copy Markdown
Owner

先夸一下 👍 这个 PR 整体完成度很高,做了一轮比较深的评审后,几个点想先肯定:

  • i18n 很扎实:10 种语言全部 18/18 key 对齐,{label} 占位符完好,messages.test.ts 对齐门通过,无重复 key,阿拉伯语也是正确的 RTL。
  • 重构很忠实reply-artifacts / conversation-message-nav 只是把原卡片包进菜单,按钮 / tooltip / badge / 删除文件静态卡、key 上移都保留得很干净,没有丢功能。
  • 嵌套右键让渡在鼠标路径下正确无回归:非文件区域的导出 / 复制菜单照常工作(对着 Radix 2.3.1 源码核实过 composeEventHandlersdefaultPrevented 语义)。
  • 纯函数 helper 都配了单测,CI 全绿。

下面把发现的问题一次性列全,方便一轮改完 🙏。就一个我建议合并前一定要修的阻塞项,其余都是小问题。


🔴 合并前建议修

1. 非文件工具行(WebFetch / Glob / Grep …)被误挂「文件右键菜单 + 错误的绝对路径 tooltip」

content-parts-renderer.tsxtoolFilePath(:2362)对每个工具都算 extractToolFilePath,其最后兜底 pathFromToolTitle(:237-256)只要标题含 /\ 或以 .ext 结尾就把标题当路径返回;而 :2728 处的包裹没有 isFilePathToolName 门控——只要 toolFilePath truthy 就套 FilePathContextMenu。于是标题喂进来后:

工具 标题示例 是否误触发
WebFetch WebFetch https://…/… 每次(URL 总含 /
Glob Glob **/*.ts 每次(含 / 或以扩展名结尾)
Grep Grep foo/bar 模式含 / 或点号时
Edit(多文件) Edit (3 files) 捕获出 "(3 files)"
TodoWrite 兜底 Todos (2/5) /

以 WebFetch / Glob 为例(几乎每次调用都会命中):

  • 悬停该行会显示错误的原生 tooltip,如 /repo/Glob **/*.ts(这算回归——原来这些行没有 title);
  • 右键弹出文件菜单;「在 Codeg 中打开」→ openFilePreview("Glob **/*.ts") 会打开一个坏的错误 tab;
  • 「复制绝对路径」复制出 /repo/WebFetch https://… 这种无意义串。

因为是运行时 UI 启发式、单测没覆盖 extractToolFilePath / pathFromToolTitle,所以 CI 绿灯照不到这块。

建议改法(两处一起):

  1. 在 :2728 的包裹上加工具名门控(把 isFilePathToolName 复用过来,和卡片内 :1971 一致):
    {toolFilePath && isFilePathToolName(normalizedToolName) ? (
    这样 WebFetch / Glob / Grep / WebSearch / TodoWrite 都会被排除。
  2. edit 本身是文件工具,多文件时仍会漏——在 extractToolFilePath 里当解析出的文件数 > 1 时返回 null,并让 pathFromToolTitle 拒绝含空格 / 括号 / (N files) 形状的串。

建议顺手补一条 extractToolFilePath 的单测(喂 WebFetch / Glob / 多文件 Edit 标题断言返回 null),把这个回归锁住。


🟠 建议一并处理(不阻塞,但体验上会踩坑)

2. macOS「用…打开 → VS Code / Cursor」实测失效

file-path-actions.ts:17openWith 映射成了 CLI 名 "code" / "cursor"。但 Tauri opener 在 macOS 上执行的是 /usr/bin/open <path> -a code,而 open -a 是按应用名解析的。我在 macOS 本机实测:

open -a code                  → Unable to find application named 'code'
open -a Code                  → Unable to find application named 'Code'
open -a cursor                → Unable to find application named 'cursor'
open -a "Visual Studio Code"  → ✅ (exit 0)

(VS Code 的 CFBundleNameCode,但 open -a 也匹配不到,只有完整应用名可以。)所以这两个菜单项在 macOS 上会恒失败、降级成 error toast。对 macOS 为主的用户来说是个可见的坏菜单项。

建议EXTERNAL_EDITOR_OPEN_WITH 按平台分档——macOS 用应用名 / bundle id(如 Visual Studio Code / Cursor,或 -b com.microsoft.VSCode),Windows / Linux 保留 code / cursor

附带一提:openPath 的 scope 被限死在 $HOME/**(代码库别处例如 skill-packs-settings / logs-settings 已经因为这个刻意改用了 revealItemInDir)。所以三个「用…打开」项对 home 目录之外的工作区也都会失败。如果想覆盖这种情况,可能得考虑 scope 或走别的通道。

3. 触屏 / 触控笔长按会同时弹出内外两个菜单

file-path-context-menu.tsx:192claimRightPointer 只在 button === 2stopPropagation;触屏长按是 button === 0,两个让渡机制又都只挂在 onContextMenu 上,盖不住 Radix 的 700ms 长按向量。结果内外菜单同时开,外层是 modal 会把外部 pointer-events 关掉,导致高 z 的内层文件菜单可见但点不动。平板 / 移动 web 上会中招。

建议:把守卫放宽到覆盖长按向量:

const claimRightPointer = useCallback((event: ReactPointerEvent) => {
  if (event.button === 2 || event.pointerType !== "mouse") event.stopPropagation()
}, [])

🟡 小问题 / nit(可跟进)

  • 标题回推的路径被重新绝对化 → 可能指向错文件content-parts-renderer.tsx:315):深层文件 /repo/deep/nested/AGENTS.md 的标题会被截成 Read nested/AGENTS.md,回推出 nested/AGENTS.md 再拼 folder.path → tooltip / 打开指向 /repo/nested/AGENTS.md(错文件)。卡片内 recovery(:1971,不带 displayTitle)不受影响,只有 header 这条。可以考虑标题兜底只用于显示文本,不喂给绝对路径 tooltip / 打开动作。
  • 面板让渡「机制 2」实际是死代码conversation-detail-panel.tsxhandlePanelContextMenu):内层 stopPropagation 在同一元素上先触发,contextmenu 根本到不了面板 div,所以这个 [data-file-path-menu] 分支不会执行。冗余但无害,建议删掉简化,或改成兼顾上面第 3 条的长按向量。
  • <span><div> 的非法嵌套:默认 trigger 渲染成 Primitive.span,几处调用点往里塞了 <div> 卡片(reply-artifacts / content-parts-renderer.tsx:2733)。靠 display:block 视觉正常、React 19 也不告警,纯 cosmetic。用 asChildSlot 合并到卡片 <div> 上就能去掉这层 wrapper。
  • openToolFileInCodeg(:2387)没有 .catchresolveOpenAbsolutePathopenFilePreview 的 try 之外,理论上可能出未捕获 rejection;同类调用点(FilePathLink.handleOpenuseOpenLinkOrFile)都包了 try/catch + toast,建议对齐。
  • 文档漂移file-path-context-menu.tsx:88-92 注释说 tool header 用 asChild,但实际所有调用点都没用到 asChild。改注释或真的用上都行。

总体是个很不错的功能 ✨。把 #1(阻塞)#2(macOS 打开) 修掉基本就可以合了;#3 建议同批修,其余归为跟进项即可。改动都不大——主要就是一个门控 + 一个平台分档 map。辛苦啦 🙌

hkx1997 added 5 commits July 23, 2026 22:53
- Only wrap Read/Edit/Write tool rows when isFilePathToolName matches
- Tighten pathFromToolTitle; disable title shortPath fallback for open/tooltip
- Multi-file edit returns no single path
- macOS openWith uses Visual Studio Code / Cursor app names
- Stop touch/pen long-press from opening the panel context menu
- Catch openFilePreview rejections on tool-row open
- Unit tests for WebFetch/Glob/multi-file false positives
Expand opener open-path and reveal scopes beyond $HOME so workspace
files on Windows drives (E:/dev/...), macOS /Volumes, and Linux paths
outside home can open with VS Code/Cursor. Normalize separators for
glob matching. openWith stays platform-specific.
opener scope entries default to Application::Default which only matches
openPath without `with`. Opening with "code"/"cursor"/app names requires
explicit "app": true (or a specific app name) on each allow entry.

Also keep broad path globs ($HOME, user dirs, /**, **) so workspaces
outside the home directory work on Windows drives, macOS volumes, and
Linux paths.
On Windows, open::with(path, "cursor") can ShellExecute-resolve to the
Cursor install directory. Prefer %LOCALAPPDATA%\\Programs\\...\\Cursor.exe
(and Code.exe for VS Code), then .cmd shims, then bare PATH names.
@hkx1997

hkx1997 commented Jul 23, 2026

Copy link
Copy Markdown
Author

建议EXTERNAL_EDITOR_OPEN_WITH 按平台分档——macOS 用应用名 / bundle id(如 Visual Studio Code / Cursor,或 -b com.microsoft.VSCode),Windows / Linux 保留 code / cursor

谢谢佬详细的指点,已完成提交

@xintaofei

Copy link
Copy Markdown
Owner

建议EXTERNAL_EDITOR_OPEN_WITH 按平台分档——macOS 用应用名 / bundle id(如 Visual Studio Code / Cursor,或 -b com.microsoft.VSCode),Windows / Linux 保留 code / cursor

谢谢佬详细的指点,已完成提交

可以的佬,PR明天再看一下,早点休息

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants